﻿using System;
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using PooledGrowableBufferHelper;

namespace WholeSlideImageLibrary.DeepZoom
{
    internal abstract class InternalDeepZoomTileSource : DeepZoomTileSource
    {

        public abstract int Width { get; }
        public abstract int Height { get; }
        public abstract int TileSize { get; }
        public abstract int Overlap { get; }

        public override string GetDefinitionFile()
        {
            switch (TileFileFormat)
            {
                case EncodedImageFileFormat.Bmp:
                    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Image xmlns=\"http://schemas.microsoft.com/deepzoom/2008\" Format=\"bmp\" Overlap=\"" + Overlap + "\" TileSize=\"" + TileSize + "\"><Size Height=\"" + Height + "\" Width=\"" + Width + "\" /></Image>";
                case EncodedImageFileFormat.Jpeg:
                    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Image xmlns=\"http://schemas.microsoft.com/deepzoom/2008\" Format=\"jpg\" Overlap=\"" + Overlap + "\" TileSize=\"" + TileSize + "\"><Size Height=\"" + Height + "\" Width=\"" + Width + "\" /></Image>";
                case EncodedImageFileFormat.Png:
                    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Image xmlns=\"http://schemas.microsoft.com/deepzoom/2008\" Format=\"png\" Overlap=\"" + Overlap + "\" TileSize=\"" + TileSize + "\"><Size Height=\"" + Height + "\" Width=\"" + Width + "\" /></Image>";
                default:
                    throw new NotSupportedException();
            }
        }

        public override async Task WriteToBufferAsync(int layer, int column, int row, IBufferWriter<byte> writer, CancellationToken cancellationToken)
        {
            using (PooledMemoryStream? stream = PooledMemoryStreamManager.Shared.GetStream())
            {
                await WriteToStreamAsync(layer, column, row, stream, cancellationToken).ConfigureAwait(false);
                Copy(stream.ToReadOnlySequence(), writer);
            }
        }

        const int MaxSegmentSize = 16384;

        private static void Copy(ReadOnlySequence<byte> sequence, IBufferWriter<byte> writer)
        {
            long length = sequence.Length;

            while (length != 0)
            {
                int segmentSize = (int)Math.Min(length, MaxSegmentSize);
                Span<byte> span = writer.GetSpan(segmentSize);
                segmentSize = (int)Math.Min(span.Length, length);
                sequence.Slice(0, segmentSize).CopyTo(span);
                writer.Advance(segmentSize);
                length -= segmentSize;
            }
        }
    }
}
